<?php

namespace App\Http\Controllers\Admin;

use App\Exceptions\ResponseException;
use App\Handler\ImageHandler;
use App\Http\Requests\Admin\Goods\addToSeckillRequest;
use App\Http\Requests\Admin\Goods\GoodsCreateRequest;
use App\Http\Requests\Admin\Goods\GoodsEditRequest;
use App\Jobs\KillGoodsClear;
use App\Models\Goods;
use App\Models\GoodsAlbum;
use App\Models\GoodsBrand;
use App\Models\GoodsCategory;
use App\Models\GoodsSku;
use App\Models\GoodsSpec;
use App\Models\GoodsType;
use App\Models\SeckillGoods;
use App\Models\Spec;
use App\Models\SpecValue;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Whoops\Exception\ErrorException;

class GoodsController extends Controller
{
	use ImageHandler;

	/**
	 * 商品列表
	 * User: Clannad ~ ☆
	 * DataTime：2019/11/17 0017 15:32
	 * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
	 */
	public function index()
	{
		$goodsList = Goods::query()
			->select('id', 'goods_name', 'category_id', 'thumb', 'brand_id', 'price', 'sort', 'status', 'inventory', 'created_at', 'kill_status')
			->with(['category' => function ($query){
				return $query->select('id', 'category_name');
			}])
			->with(['brand' => function ($query){
				return $query->select('id', 'brand_name');
			}])
			->orderBy('id', 'desc')
			->paginate(10);

		return view('admin.goods.index', compact('goodsList'));
	}

	/**
	 * Show the form for creating a new resource.
	 *
	 * @return \Illuminate\Http\Response
	 */
	public function create()
	{
		$category = GoodsCategory::all()->toArray();
		$category = getTree($category);

		$typeList = GoodsType::query()
			->select('type_name', 'id')
			->get()
			->toArray();

		$brand = GoodsBrand::all()->toArray();

		return view('admin.goods.add', compact('category', 'brand', 'typeList'));
	}

	/**
	 * Store a newly created resource in storage.
	 *
	 * @param \Illuminate\Http\Request $request
	 * @return \Illuminate\Http\Response
	 */
	public function store(GoodsCreateRequest $request)
	{
		$data = $request->except('_token');

		$goodsData = [
			'goods_name'  => $data['goods_name'],
			'category_id' => $data['category_id'],
			'brand_id'    => $data['brand_id'],
			'sort'        => $data['sort'],
			'status'      => $data['status'],
			'type_id'     => $data['type_id'],
			'description' => $data['description'],
		];

		//判断是否有文件
		if ($request->hasFile('thumb')) {

//            $uploadImage = $this->imageUploader('uploads/goods', 'thumb');
			$uploadImage = $this->upLoadToAliOss('thumb', 'goods_thumb/');

			if ($uploadImage['status'] == '200') {
				$goodsData['thumb'] = $uploadImage['path'];
			} else {
				throw new ResponseException(422, $uploadImage['msg']);
			}
		}

		\DB::beginTransaction();

		$goods = Goods::create($goodsData);

		try {
			if ($goods->id) {
				//开始插入商品属性
				if (isset($data['spec'])) {
					$specList = [];
					foreach ($data['spec'] as $k => $v) {
						if (is_array($v)) {
							foreach ($v as $k1 => $spec_id) {
								$item       = [
									'spec_id'       => $k,
									'spec_value_id' => $spec_id
								];
								$specList[] = $item;
							}
						}
					}

					//关联插入商品属性
					$goods->goods_spec()->createMany($specList);
				}

				//插入sku
				if (isset($data['sku'])) {

					//库存总量
					$inventory_total = 0;
					//价格排序
					$priceArr = [];

					$skuList = [];
					foreach ($data['sku'] as $k => $v) {
						$item            = [
							'sku_list'  => $v,
							'price'     => $data['sku_price'][$k] ?? 0,
							'inventory' => $data['sku_num'][$k] ?? 0,
						];
						$skuList[]       = $item;
						$inventory_total += $item['inventory'];
						$priceArr[]      = $item['price'];
					}

					$goods->goods_sku()->createMany($skuList);

					//将价格排序并过滤拿到最低的价格
					sort($priceArr, SORT_NUMERIC);

					$priceArr = array_values(array_filter($priceArr, function ($item){
						if ($item != '0.00' && $item != '0') {
							return $item;
						}
					}));

					$data = [];

					if ($inventory_total) {
						$data['inventory'] = $inventory_total;

					} else {
						$data['status'] = 0;
					}

					if ( ! $priceArr) {
						$data['price']  = 0;
						$data['status'] = 0;
					} else {
						$data['price'] = $priceArr['0'];
					}

					//更新库存到商品主表
					$goods->where('id', $goods->id)->update($data);
				} else {
					$goods->price  = 0;
					$goods->status = 0;
					$goods->save();
				}

				//处理相册
				if ($request->hasFile('album')) {
//                    $uploadImages = $this->batchImageUploader('uploads/goods_album', 'album');
					$uploadImages = $this->batchUploadToAliOss('album', 'goods_album/', 'goods_' . $goods->id . '_');
					if ($uploadImages['status'] == '200') {
						$album = [];
						foreach ($uploadImages['imgs'] as $k => $v) {
							if ($v) {
								$album[] = [
									'image' => $v,
								];
							}
						}

						$goods->goods_album()->createMany($album);
					} else {
						\DB::rollBack();
						throw new ResponseException(422, $uploadImages['msg']);
					}
				}
				\DB::commit();
				throw new ResponseException(200, '添加成功');
			} else {
				\DB::rollBack();
				throw new ResponseException(500, '添加失败，请联系管理员');
			}
		} catch (ErrorException $exception) {
			\DB::rollBack();
		}
	}

	/**
	 * Show the form for editing the specified resource.
	 *
	 * @param int $id
	 * @return \Illuminate\Http\Response
	 */
	public function edit($id)
	{
		$goods = Goods::findOrfail($id);

		$typeList = GoodsType::query()
			->select('type_name', 'id')
			->get()
			->toArray();

		//取商品的规格信息
		// 先根据分类 取出所有的规格信息
		$spec = Spec::query()
			->select('id', 'spec_name')
			->with(['spec_value' => function ($query){
				return $query->select('id', 'value', 'spec_id');
			}])
			->where('type_id', $goods->type_id)
			->get()->toArray();

		//商品已有的规格
		$goodsSpec = GoodsSpec::query()
			->select('goods_id', 'spec_id', 'spec_value_id')
			->where('goods_id', $id)
			->get()->toArray();

		//合并成一个数组，给前台判断
		$goodsSpecArr = array_column($goodsSpec, 'spec_value_id');

		//取商品的库存信息
		$goods_sku = GoodsSku::query()
			->select('goods_id', 'sku_list', 'inventory', 'price')
			->where('goods_id', $id)
			->get()->toArray();

		$sku_str = array_column($goods_sku, 'sku_list');

		//商品已经存在的sku信息
		$skuSpecInfo = [];
		foreach ($sku_str as $k => $v) {
			$skuSpecInfo[] = SpecValue::query()
				->select('id', 'value')
				->whereIn('id', explode(',', $v))
				->get()->toArray();
		}

		//分类
		$category = GoodsCategory::all()->toArray();
		$category = getTree($category);

		//品牌
		$brand = GoodsBrand::all()->toArray();

		//相册
		$goods_album = $goods->goods_album()->select('id', 'goods_id', 'image')->get()->toArray();

		return view('admin.goods.edit', compact('category', 'brand', 'goods', 'spec', 'goodsSpecArr', 'skuSpecInfo', 'goods_sku', 'sku_str', 'typeList', 'goods_album'));
	}

	/**
	 * 更新信息
	 * User: Clannad ~ ☆
	 * DataTime：2019/11/17 0017 12:19
	 * @param GoodsCreateRequest $request
	 * @param $id
	 */
	public function update(GoodsEditRequest $request, $id)
	{
		$goods = Goods::findOrfail($id);
		//先接收主要信息
		$data = $request->except('_token');

		//先更新产品信息
		$goodsData = [
			'goods_name'  => $data['goods_name'],
			'category_id' => $data['category_id'],
			'brand_id'    => $data['brand_id'],
			'sort'        => $data['sort'],
			'status'      => $data['status'],
			'description' => $data['description'],
		];

		\DB::beginTransaction();

		try {
			//判断图片
			if ($request->hasFile('thumb')) {
//				$uploadImg = $this->imageUploader('uploads/goods', 'thumb');
				$uploadImg = $this->upLoadToAliOss('thumb','goods_thumb/');
				if ($uploadImg['status'] == 200) {
//					$host = getHttpType() . $_SERVER['SERVER_NAME'];

					$goodsData['thumb'] = $uploadImg['path'];

					//删除原图
//					$goods->thumb = str_replace($host, '', $goods->thumb);
//					@unlink(public_path() . '/' . $goods->thumb);

					$file_name = $this->cutFileName($goods->thumb);
					$this->delAliOssFile($file_name);
				} else {
					throw new ResponseException(422, $uploadImg['msg']);
				}
			}

			$goods->fill($goodsData);
			if ($goods->save()) {
				//更新规格信息
				if (isset($data['spec']) && isset($data['sku']) && isset($data['sku_num']) && isset($data['sku_price'])) {

					$specList = [];
					foreach ($data['spec'] as $k => $v) {
						if (is_array($v)) {
							foreach ($v as $k1 => $spec_id) {
								$item       = [
									'spec_id'       => $k,
									'spec_value_id' => $spec_id
								];
								$specList[] = $item;
							}
						}
					}

					//关联更新规格
					$goods->goods_spec()->delete();
					$goods->goods_spec()->createMany($specList);

					//更新库存
					if (isset($data['sku'])) {
						//库存总量
						$inventory_total = 0;
						//价格排序
						$priceArr = [];

						$skuList = [];
						foreach ($data['sku'] as $k => $v) {
							$item            = [
								'goods_id'  => $id,
								'sku_list'  => $v,
								'price'     => $data['sku_price'][$k] ?? 0,
								'inventory' => $data['sku_num'][$k] ?? 0,
							];
							$skuList[]       = $item;
							$inventory_total += $item['inventory'];
							$priceArr[]      = $item['price'];
						}

						$goods->goods_sku()->delete();
						$goods->goods_sku()->createMany($skuList);

						//将价格排序并过滤拿到最低的价格
						sort($priceArr, SORT_NUMERIC);
						$priceArr = array_values(array_filter($priceArr, function ($item){
							if ($item != '0.00' && $item != '0') {
								return $item;
							}
						}));

						$data = [];

						if ($inventory_total) {
							$data['inventory'] = $inventory_total;
						} else {
							$data['status'] = 0;
						}

						if ( ! $priceArr) {
							$data['price']  = 0;
							$data['status'] = 0;
						} else {
							$data['price'] = $priceArr['0'];
						}

						//更新库存到商品主表
						$goods->where(['id' => $id])->sharedLock()->update($data);
					}
				} else {
					//如果没有规格信息上传，可能规格已经全部删除了，那么，这里就要处理掉全部规格信息
					$goods->goods_spec()->delete();
					$goods->goods_sku()->delete();

					$goods->where(['id' => $id])->sharedLock()->update([
						'inventory' => 0,
						'price'     => 0,
						'status'    => 0, //商品下架
					]);
				}

				//处理相册
				if ($request->hasFile('album')) {
					$uploadImages = $this->batchUploadToAliOss('album', 'goods_album/', 'goods_' . $goods->id . '_');
					if ($uploadImages['status'] == '200') {
						$album = [];
						foreach ($uploadImages['imgs'] as $k => $v) {
							if ($v) {
								$album[] = [
									'image' => $v,
								];
							}
						}

						$goods->goods_album()->createMany($album);
					} else {
						throw new ResponseException(422, $uploadImages['msg']);
					}
				}

				\DB::commit();
				return response()->json(['code'=>200,'msg'=>'修改成功']);
			} else {
				\DB::rollBack();
				throw new ResponseException(500, '修改失败');
			}
		} catch (\Exception $e) {
			\DB::rollBack();
			Log::info('修改商品出错---id---' . $id . '错误信息是：' . $e->getMessage());
			throw new ResponseException(500, '内部服务器出错了');
		}

	}

	/**
	 * ajax删除图片
	 * User: Clannad ~ ☆
	 * DataTime：2019/11/18 0018 12:01
	 */
	public function ajaxDeleteImage()
	{
		$id = \request()->input('id');

		$img = GoodsAlbum::find($id);

		if ( ! $img) {
			throw new ResponseException(404, '相册图片不存在');
		}

		//本地存储时
//		$host = getHttpType() . $_SERVER['SERVER_NAME'];
//		$img->image = str_replace($host, '', $img->image);
//		@unlink(public_path() . '/' . $img->image);

		$file_name = $this->cutFileName($img->image);
		$this->delAliOssFile($file_name);

		if ($img->delete()) {
			throw new ResponseException(200, '删除成功');
		} else {
			throw new ResponseException(500, '删除失败');
		}

	}

	/**
	 * Remove the specified resource from storage.
	 *
	 * @param int $id
	 * @return \Illuminate\Http\Response
	 */
	public function destroy($id)
	{
		//
	}


	/**
	 * ajax获取规格列表
	 * User: Clannad ~ ☆
	 * DataTime：2019/11/16 0016 10:54
	 * @param Request $request
	 * @throws ResponseException
	 */
	public function ajaxGetSpec(Request $request)
	{
		//查询分类是否存在，再查询分类绑定的规格是否有数据
		$type_id = $request->input('type_id', 0);
		if ( ! GoodsType::find($type_id)) {
			throw new ResponseException('404', '分类不存在');
		}

		$specList = Spec::query()
			->select('id', 'spec_name')
			->with(['spec_value' => function ($query){
				return $query->select('id', 'spec_id', 'value');
			}])
			->where('type_id', $type_id)
			->get()->toArray();

		if (count($specList) > 0) {
			throw new ResponseException(200, 'ok', $specList);
		} else {
			throw new ResponseException(404, '不存在属性数据');
		}
		/*$list = $specList->map(function ($item){
			return [
				'id'=>$item->id,
				'spec_name'=>$item->spec_name,
				'child'=>
					$item->spec_value->map(function ($child){
						return [
							'id'=>$child->id,
							'spec_value'=>$child->value,
						];
					})->toArray()

			];
		});
		echo '<pre>';
		var_dump($list->toArray());
		die;*/
	}


	/**
	 * ajax获得库存的笛卡尔积
	 * User: Clannad ~ ☆
	 * DataTime：2019/11/16 0016 15:23
	 * @throws ResponseException
	 */
	public function ajaxGetStockDicar()
	{
		$ids = \request()->input('ids');
		$ids = json_decode($ids, true);

		$spec_values = SpecValue::query()
			->select('id', 'spec_id', 'value')
			->whereIn('id', $ids)
			->get()->toArray();

		if ($spec_values) {
			$specList = [];
			foreach ($spec_values as $k => $v) {
				$specList[$v['spec_id']][] = $v;
			}

			foreach ($specList as $k => $v) {
				//优化写法
				$specList[$k] = array_column($v, 'value', 'id');
//			    $arr = [];
//			    foreach ($v as $k1 => $v1) {
//			        $arr[$v1['id']] = $v1['value'];
//			    }
//
//			    $specList[$k] = $arr;
			}

			//库存的笛卡尔积
			$dicar = dicarer($specList);

			throw new ResponseException(200, 'ok', $dicar);
		} else {
			throw new ResponseException(404, '没有规格数据');
		}
	}


	/**
	 * 编辑时，获取最新的库存到修改后的dom中
	 * User: Clannad ~ ☆
	 * DataTime：2019/11/17 0017 10:08
	 */
	public function ajaxGetNowInventory()
	{
		$param = \request()->all();

		$sku = GoodsSku::query()
			->select('inventory', 'price')
			->where([
				'goods_id' => $param['goods_id'],
				'sku_list' => $param['sku_list']
			])
			->first();

		if ($sku) {
			throw new ResponseException(200, 'ok', [
				'inventory' => $sku->inventory,
				'price'     => $sku->price,
			]);
		}
	}

	/**
	 * 加入秒杀
	 * User: Clannad ~ ☆
	 * Date: 2020/3/9 0009 22:44
	 * @param Request $request
	 * @param $id
	 */
	public function addToSecKill(Request $request)
	{
		$id = $request->input('id');

		//判断当前商品是否已经加入秒杀
		$check = SeckillGoods::query()->where([
		    'goods_id' => $id,
            'kill_status'=>1,
        ])->first();

		if ($check) {
			return back()->with('danger', '当前商品已经加入秒杀，请选择其他商品');
		}

		$goods = Goods::query()
			->select('id', 'goods_name', 'thumb', 'price', 'inventory')
            ->where(function ($query) use ($id){
                $query->where([
                    'status'=>1,
                    'id'=>$id,
                    ['inventory','>',0],
                ]);
            })
			->first();

		if(!$goods){
		    return back()->with('danger','下架和库存为0的商品不能加入活动');
		}

		return view('admin.goods.add_to_seckill', compact('goods'));
	}

	/**
	 * 秒杀商品加入数据库
	 * User: Clannad ~ ☆
	 * Date: 2020/3/10 0010 12:03
	 */
	public function addSeckillGoods(addToSeckillRequest $request)
	{
		$data = $request->except('_token');

		$insertData = [
			'goods_id'      => $data['goods_id'],
			'goods_name'    => $data['goods_name'],
			'thumb'         => $data['thumb'],
			'price'         => $data['price'],
			'seckill_price' => $data['seckill_price'],
			'kill_status'   => $data['kill_status'],
			'single_num'    => $data['single_num'],
			'start_time'    => $data['start_time'],
			'end_time'      => $data['end_time'],
			'inventory'     => $data['inventory'],
//			'sale_num'      => $data['sale_num'],
			'sort'          => $data['sort'],
		];

		//判断时间的选中
		if ($insertData['end_time'] < $insertData['start_time'] || $insertData['end_time'] == $insertData['start_time']) {
			return redirect()->back()->with('info', '结束时间不能早于开始时间，或者相同');
		}

		//秒杀活动的设定时间最多提前48小时 活动必须是同一天
        $start_time = strtotime($insertData['start_time']);
        $end_time = strtotime($insertData['end_time']);

        if($start_time - time() > 172800){
            return redirect()->back()->with('info','活动开始时间只能提前2天设置');
        }

        if($start_time < time() || $end_time < time()){
            return redirect()->back()->with('info','活动开始时间不能早于当前时间');
        }

        //获得活动当天的开始和结束时间
        $activity_day = [
            '0'=>strtotime(date('Y-m-d 00:00:00',$start_time)),
            '1'=>strtotime(date('Y-m-d 23:59:59',$start_time)),
        ];

        if($start_time < $activity_day['0'] || $end_time > $activity_day['1']){
           return redirect()->back()->with('info','开始时间和结束时间必须是同一天');
        }

		$goods = Goods::query()->where([
		    'id'=>$insertData['goods_id']
        ])->firstOrFail();

		//判断库存与商品库存必须一致
		if ($insertData['inventory'] > $goods['inventory']) {
			return back()->with('danger', '秒杀库存不能大于商品库存');
		}

		\DB::beginTransaction();

        try {
            //添加到秒杀表
            $seckill = SeckillGoods::create($insertData);
            $goods->kill_status = 1;
            $goods->save();

            \DB::commit();
        }catch (\Exception $exception){
            \DB::rollBack();
            Log::info('加入秒杀错误' . $exception->getMessage());
            return back()->with('danger','加入错误秒杀出错了，请联系管理员');
        }


        //从当前时间算，到结束时间
        $expired_time = strtotime($insertData['end_time']) - time();
        //秒杀库存入队
        $key = 'seckill_goods_stock:goods_id:' . $insertData['goods_id'].':active_id:'.$seckill->id;
        for ($i = 0; $i < $insertData['inventory']; $i++) {
            Redis::lpush($key, 1);
            //设置key的过期时间
            Redis::expire($key, $expired_time);
        }

		//取出每一个组合的规格sku，放置在hash中
		$goods_sku = GoodsSku::query()
			->select('sku_list', 'goods_id', 'inventory')
			->where('goods_id', $insertData['goods_id'])
			->get()->toArray();

        //从当前时间算，到结束时间
        $expired_time = strtotime($insertData['end_time']) - time();

        $sku_arr = array_column($goods_sku,'inventory','sku_list');

        //将sku存入hash表中的
        $key = 'goods_sku_stock:goods_id:'.$insertData['goods_id'].':active_id:'.$seckill->id;
        Redis::hmset($key,$sku_arr);
        Redis::expire($key, $expired_time);
        /*foreach ($goods_sku as $k => $v) {
            //推送到库存队列
            $key = 'seckill_goods_stock:goods_id:' . $insertData['goods_id'] . ':sku:' . $v['sku_list'];
            for ($i = 0; $i < $v['inventory']; $i++) {
                Redis::lpush($key, json_encode(['sku_list' => $v['sku_list']]));
                //设置key的过期时间
            }
            Redis::expire($key, $expired_time);
		}*/
		//向延时队列派发任务
        KillGoodsClear::dispatch($seckill->id)->delay($expired_time);

		return redirect()->route('admin.goods.index')->with('info', '加入秒杀成功');
	}
}
